package it.bela.pgcdc.common.dao.impl;

import static org.hibernate.criterion.Example.create;

import java.util.List;

import it.bela.pgcdc.common.bean.BaseBean;
import it.bela.pgcdc.common.dao.BaseDao;

import org.apache.log4j.Logger;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public abstract class AbstractBaseDao<T> implements BaseDao<T> {
	
	private static final Logger log = Logger.getLogger(AbstractBaseDao.class);

	@Autowired(required = true)
	private SessionFactory sessionFactory;

	public void persist(T transientInstance) {
		log.debug("persisting " + transientInstance.getClass().getName() + " instance");
		try {
			sessionFactory.getCurrentSession().persist(transientInstance);
			log.debug("persist successful");
		} catch (RuntimeException re) {
			log.error("persist failed", re);
			throw re;
		}
	}
	
	public void save(T instance) {
		log.debug("saving " + instance.getClass().getName() + " instance");
		try {
			sessionFactory.getCurrentSession().save(instance);
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
	}

	public void attachDirty(T instance) {
		log.debug("attaching dirty " + instance.getClass().getName() + " instance");
		try {
			sessionFactory.getCurrentSession().saveOrUpdate(instance);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void attachClean(T instance) {
		log.debug("attaching clean " + instance.getClass().getName() + " instance");
		try {
			sessionFactory.getCurrentSession().lock(instance, LockMode.NONE);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void delete(T persistentInstance) {
		log.debug("deleting " + persistentInstance.getClass().getName() + " instance");
		try {
			sessionFactory.getCurrentSession().delete(persistentInstance);
			log.debug("delete successful");
		} catch (RuntimeException re) {
			log.error("delete failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public T merge(T detachedInstance) {
		log.debug("merging " + detachedInstance.getClass().getName() + " instance");
		try {
			
			T result = (T) sessionFactory.getCurrentSession().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	@SuppressWarnings("unchecked")
	public T findById(Class<? extends BaseBean> classe, Long id) {
		log.debug("getting " + classe.getName() + " instance with id: " + id);
		try {
					
			T instance = (T) sessionFactory
					.getCurrentSession().get(classe, id);
			if (instance == null) {
				log.debug("get successful, no instance found");
			} else {
				log.debug("get successful, instance found");
			}
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByExample(T instance) {
		log.debug("finding " + instance.getClass().getName() + " instance by example");
		try {
			List<T> results = (List<T>) sessionFactory
					.getCurrentSession().createCriteria(instance.getClass())
					.add(create(instance)).list();
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (RuntimeException re) {
			log.error("find by example failed", re);
			throw re;
		}
	}
}
